package com.zc.nettycilent.connect;

import com.zc.nettycilent.utils.SpringContextUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import io.netty.util.Timer;
import io.netty.util.TimerTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;

import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@ChannelHandler.Sharable
@Slf4j
public abstract class MiiClientConnect extends ChannelInboundHandlerAdapter implements TimerTask {

    private static ApplicationContext applicationContext = SpringContextUtil.applicationContext;

    private int timeOut = 5000;

    private final Bootstrap boot;
    private Timer timer;

    /**
     * 重连地址
     */
    private Map<Timeout, SocketAddress> timeoutAddressMap = new HashMap<>();

    public MiiClientConnect(Bootstrap boot, int timeOut) {
        this.boot = boot;
        this.timeOut = timeOut;
        this.timer = new HashedWheelTimer();
    }

    public ChannelFuture connect(SocketAddress remoteAddress) throws Exception {
        ChannelFuture future = boot.connect(remoteAddress);
        future.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                if (!channelFuture.isSuccess()) {
                    future.channel().eventLoop().schedule((Runnable) () -> {
                        applicationContext.publishEvent("连接失败");
                        try {
                            connect(remoteAddress);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }, timeOut, TimeUnit.MILLISECONDS);
                } else {
                    //服务器连接成功
                    afterSuccess();
                    applicationContext.publishEvent("上线了");
                }
            }
        });
        future.channel().closeFuture().sync();
        return future;
    }

    @Override
    public void run(Timeout timeout) throws Exception {
        log.info("enter... 开始重连");
        // 这里添加断线重连逻辑
        SocketAddress socketAddress = timeoutAddressMap.get(timeout);
        if (socketAddress != null) {
            connect(socketAddress);
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        Timeout timeout = timer.newTimeout(this, timeOut, TimeUnit.MILLISECONDS);
        timeoutAddressMap.put(timeout, channel.remoteAddress());
        // 发布服务器断开连接事件
        applicationContext.publishEvent("服务器下线了");
        // 连接断开后的最后处理
        ctx.pipeline().remove(ctx.handler());
        ctx.deregister();
        ctx.close();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("与服务器建立连接");
        super.channelActive(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("连接异常: ", cause);
        ctx.close();
    }

    protected abstract void afterSuccess();

}
